@xrmforge/testing 0.1.0 → 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 XrmForge Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.d.ts CHANGED
@@ -25,6 +25,11 @@ declare class MockAttribute {
25
25
  removeOnChange(handler: OnChangeHandler): void;
26
26
  /** @internal Get registered onChange handlers (for testing/event simulation) */
27
27
  getOnChangeHandlers(): readonly OnChangeHandler[];
28
+ /**
29
+ * Fire all registered onChange handlers with the given event context.
30
+ * @internal Called by FormMock.fireOnChange(fieldName)
31
+ */
32
+ fireOnChange(eventContext: Xrm.Events.EventContext): void;
28
33
  getAttributeType(): string;
29
34
  getFormat(): string | null;
30
35
  getParent(): Xrm.Entity;
@@ -130,6 +135,11 @@ interface FormMock<TForm> {
130
135
  asEventContext(): Xrm.Events.EventContext;
131
136
  /** Create an Xrm.Events.EventContext with getEventSource (for onChange handlers) */
132
137
  asAttributeEventContext(fieldName: string): Xrm.Events.EventContext;
138
+ /**
139
+ * Fire all registered onChange handlers for a field.
140
+ * Creates an EventContext with the attribute as event source and calls all handlers.
141
+ */
142
+ fireOnChange(fieldName: string): void;
133
143
  }
134
144
 
135
145
  /**
@@ -201,4 +211,58 @@ declare class MockEventContext {
201
211
  setSharedVariable(_key: string, _value: unknown): void;
202
212
  }
203
213
 
204
- export { type CreateFormMockOptions, type FormMock, type FormNotification, MockAttribute, MockControl, MockEntity, MockEventContext, MockUi, createFormMock };
214
+ /**
215
+ * @xrmforge/testing - Global Xrm Mock Setup
216
+ *
217
+ * Sets up a minimal global Xrm object for testing form scripts that
218
+ * call Xrm.WebApi, Xrm.Navigation, or Xrm.Utility.
219
+ *
220
+ * Usage in vitest setup or beforeEach:
221
+ * ```typescript
222
+ * import { setupXrmMock, teardownXrmMock } from '@xrmforge/testing';
223
+ *
224
+ * beforeEach(() => setupXrmMock());
225
+ * afterEach(() => teardownXrmMock());
226
+ * ```
227
+ *
228
+ * All WebApi methods return resolved promises with empty results by default.
229
+ * Override specific methods in your test:
230
+ * ```typescript
231
+ * setupXrmMock({
232
+ * webApiOverrides: {
233
+ * retrieveMultipleRecords: async () => ({ entities: [{ name: 'Test' }] }),
234
+ * },
235
+ * });
236
+ * ```
237
+ */
238
+ /** Options for customizing the Xrm mock */
239
+ interface SetupXrmMockOptions {
240
+ /** Override specific Xrm.WebApi methods */
241
+ webApiOverrides?: Partial<{
242
+ retrieveRecord: (entityType: string, id: string, options?: string) => Promise<Record<string, unknown>>;
243
+ retrieveMultipleRecords: (entityType: string, options?: string) => Promise<{
244
+ entities: Record<string, unknown>[];
245
+ }>;
246
+ createRecord: (entityType: string, data: Record<string, unknown>) => Promise<{
247
+ id: string;
248
+ }>;
249
+ updateRecord: (entityType: string, id: string, data: Record<string, unknown>) => Promise<{
250
+ id: string;
251
+ }>;
252
+ deleteRecord: (entityType: string, id: string) => Promise<{
253
+ id: string;
254
+ }>;
255
+ }>;
256
+ }
257
+ /**
258
+ * Set up a global Xrm mock for testing.
259
+ * Call in beforeEach() or vitest setup file.
260
+ */
261
+ declare function setupXrmMock(options?: SetupXrmMockOptions): void;
262
+ /**
263
+ * Remove the global Xrm mock.
264
+ * Call in afterEach() to clean up.
265
+ */
266
+ declare function teardownXrmMock(): void;
267
+
268
+ export { type CreateFormMockOptions, type FormMock, type FormNotification, MockAttribute, MockControl, MockEntity, MockEventContext, MockUi, type SetupXrmMockOptions, createFormMock, setupXrmMock, teardownXrmMock };
package/dist/index.js CHANGED
@@ -48,6 +48,15 @@ var MockAttribute = class {
48
48
  getOnChangeHandlers() {
49
49
  return this._onChangeHandlers;
50
50
  }
51
+ /**
52
+ * Fire all registered onChange handlers with the given event context.
53
+ * @internal Called by FormMock.fireOnChange(fieldName)
54
+ */
55
+ fireOnChange(eventContext) {
56
+ for (const handler of this._onChangeHandlers) {
57
+ handler(eventContext);
58
+ }
59
+ }
51
60
  getAttributeType() {
52
61
  return "string";
53
62
  }
@@ -223,11 +232,14 @@ var MockUi = class {
223
232
  setLabel: () => {
224
233
  },
225
234
  getParent: () => ({}),
235
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Xrm.Collection overloaded get()
226
236
  controls: { forEach: () => {
227
237
  }, get: (() => null), getLength: () => 0 }
238
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Xrm.Collection overloaded get()
228
239
  })),
229
240
  getLength: () => 0
230
241
  },
242
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Xrm.Collection overloaded get()
231
243
  controls: { forEach: () => {
232
244
  }, get: (() => null), getLength: () => 0 }
233
245
  }),
@@ -358,6 +370,7 @@ function createFormMock(values = {}, options = {}) {
358
370
  attributes: {
359
371
  forEach: () => {
360
372
  },
373
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Xrm.Collection.ItemCollection has complex overloaded get()
361
374
  get: (() => null),
362
375
  getLength: () => attributes.size
363
376
  }
@@ -389,15 +402,64 @@ function createFormMock(values = {}, options = {}) {
389
402
  formContext,
390
403
  getOrCreateAttribute(fieldName)
391
404
  );
405
+ },
406
+ fireOnChange(fieldName) {
407
+ const attr = getOrCreateAttribute(fieldName);
408
+ const eventContext = new MockEventContext(
409
+ formContext,
410
+ attr
411
+ );
412
+ attr.fireOnChange(eventContext);
392
413
  }
393
414
  };
394
415
  }
416
+
417
+ // src/setup-xrm-mock.ts
418
+ function setupXrmMock(options) {
419
+ const webApi = {
420
+ retrieveRecord: options?.webApiOverrides?.retrieveRecord ?? (async () => ({})),
421
+ retrieveMultipleRecords: options?.webApiOverrides?.retrieveMultipleRecords ?? (async () => ({ entities: [] })),
422
+ createRecord: options?.webApiOverrides?.createRecord ?? (async () => ({ id: "00000000-0000-0000-0000-000000000000" })),
423
+ updateRecord: options?.webApiOverrides?.updateRecord ?? (async () => ({ id: "00000000-0000-0000-0000-000000000000" })),
424
+ deleteRecord: options?.webApiOverrides?.deleteRecord ?? (async () => ({ id: "00000000-0000-0000-0000-000000000000" }))
425
+ };
426
+ const navigation = {
427
+ openAlertDialog: async () => ({}),
428
+ openConfirmDialog: async () => ({ confirmed: false }),
429
+ openForm: async () => ({ savedEntityReference: [] }),
430
+ openFile: async () => void 0
431
+ };
432
+ const utility = {
433
+ showProgressIndicator: () => void 0,
434
+ closeProgressIndicator: () => void 0,
435
+ getGlobalContext: () => ({
436
+ getClientUrl: () => "https://test.crm4.dynamics.com",
437
+ organizationSettings: { uniqueName: "testorg" },
438
+ userSettings: {
439
+ userId: "{00000000-0000-0000-0000-000000000001}",
440
+ userName: "Test User",
441
+ languageId: 1033
442
+ }
443
+ })
444
+ };
445
+ const xrmMock = {
446
+ WebApi: webApi,
447
+ Navigation: navigation,
448
+ Utility: utility
449
+ };
450
+ globalThis["Xrm"] = xrmMock;
451
+ }
452
+ function teardownXrmMock() {
453
+ delete globalThis["Xrm"];
454
+ }
395
455
  export {
396
456
  MockAttribute,
397
457
  MockControl,
398
458
  MockEntity,
399
459
  MockEventContext,
400
460
  MockUi,
401
- createFormMock
461
+ createFormMock,
462
+ setupXrmMock,
463
+ teardownXrmMock
402
464
  };
403
465
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mock-attribute.ts","../src/mock-control.ts","../src/mock-entity.ts","../src/mock-ui.ts","../src/mock-event-context.ts","../src/create-form-mock.ts"],"sourcesContent":["/**\n * @xrmforge/testing - MockAttribute\n *\n * Mock implementation of Xrm.Attributes.Attribute.\n * Tracks value changes, required level, submit mode, and onChange handlers.\n */\n\ntype OnChangeHandler = (context: Xrm.Events.EventContext) => void;\n\nexport class MockAttribute {\n private _name: string;\n private _value: unknown;\n private _initialValue: unknown;\n private _requiredLevel: Xrm.Attributes.RequirementLevel = 'none';\n private _submitMode: Xrm.SubmitMode = 'dirty';\n private _onChangeHandlers: OnChangeHandler[] = [];\n\n constructor(name: string, value: unknown = null) {\n this._name = name;\n this._value = value;\n this._initialValue = value;\n }\n\n getName(): string {\n return this._name;\n }\n\n getValue(): unknown {\n return this._value;\n }\n\n setValue(value: unknown): void {\n this._value = value;\n }\n\n getIsDirty(): boolean {\n return this._value !== this._initialValue;\n }\n\n getRequiredLevel(): Xrm.Attributes.RequirementLevel {\n return this._requiredLevel;\n }\n\n setRequiredLevel(level: Xrm.Attributes.RequirementLevel): void {\n this._requiredLevel = level;\n }\n\n getSubmitMode(): Xrm.SubmitMode {\n return this._submitMode;\n }\n\n setSubmitMode(mode: Xrm.SubmitMode): void {\n this._submitMode = mode;\n }\n\n addOnChange(handler: OnChangeHandler): void {\n this._onChangeHandlers.push(handler);\n }\n\n removeOnChange(handler: OnChangeHandler): void {\n const index = this._onChangeHandlers.indexOf(handler);\n if (index >= 0) {\n this._onChangeHandlers.splice(index, 1);\n }\n }\n\n /** @internal Get registered onChange handlers (for testing/event simulation) */\n getOnChangeHandlers(): readonly OnChangeHandler[] {\n return this._onChangeHandlers;\n }\n\n getAttributeType(): string {\n return 'string';\n }\n\n getFormat(): string | null {\n return null;\n }\n\n getParent(): Xrm.Entity {\n return {} as Xrm.Entity;\n }\n\n getUserPrivilege(): Xrm.Privilege {\n return { canRead: true, canUpdate: true, canCreate: true };\n }\n\n controls: Xrm.Collection.ItemCollection<Xrm.Controls.Control> = {\n forEach: () => {},\n get: (() => null) as unknown as Xrm.Collection.ItemCollection<Xrm.Controls.Control>['get'],\n getLength: () => 0,\n };\n}\n","/**\n * @xrmforge/testing - MockControl\n *\n * Mock implementation of Xrm.Controls.StandardControl.\n * Tracks visibility, disabled state, label, and notifications.\n */\n\nexport class MockControl {\n private _name: string;\n private _visible: boolean = true;\n private _disabled: boolean = false;\n private _label: string = '';\n private _notifications: Map<string, string> = new Map();\n\n constructor(name: string) {\n this._name = name;\n }\n\n getName(): string {\n return this._name;\n }\n\n getVisible(): boolean {\n return this._visible;\n }\n\n setVisible(visible: boolean): void {\n this._visible = visible;\n }\n\n getDisabled(): boolean {\n return this._disabled;\n }\n\n setDisabled(disabled: boolean): void {\n this._disabled = disabled;\n }\n\n getLabel(): string {\n return this._label;\n }\n\n setLabel(label: string): void {\n this._label = label;\n }\n\n setNotification(message: string, uniqueId?: string): boolean {\n this._notifications.set(uniqueId ?? '_default', message);\n return true;\n }\n\n clearNotification(uniqueId?: string): boolean {\n this._notifications.delete(uniqueId ?? '_default');\n return true;\n }\n\n /** @internal Get all notifications (for assertions) */\n getNotifications(): ReadonlyMap<string, string> {\n return this._notifications;\n }\n\n getControlType(): string {\n return 'standard';\n }\n\n getParent(): Xrm.Controls.Section {\n return {} as Xrm.Controls.Section;\n }\n\n /** Lookup-specific: addPreSearch (no-op for non-lookup controls) */\n addPreSearch(_handler: () => void): void {\n // no-op\n }\n\n /** Lookup-specific: addCustomFilter (no-op for non-lookup controls) */\n addCustomFilter(_filter: string, _entityLogicalName?: string): void {\n // no-op\n }\n}\n","/**\n * @xrmforge/testing - MockEntity\n *\n * Mock implementation of Xrm.Entity.\n */\n\nimport type { MockAttribute } from './mock-attribute.js';\n\nconst NULL_GUID = '00000000-0000-0000-0000-000000000000';\n\nexport class MockEntity {\n private _id: string;\n private _entityName: string;\n private _attributes: Map<string, MockAttribute>;\n\n constructor(\n entityName: string,\n entityId: string,\n attributes: Map<string, MockAttribute>,\n ) {\n this._entityName = entityName;\n this._id = entityId || NULL_GUID;\n this._attributes = attributes;\n }\n\n getId(): string {\n return `{${this._id}}`;\n }\n\n getEntityName(): string {\n return this._entityName;\n }\n\n getEntityReference(): Xrm.LookupValue {\n return {\n id: this._id,\n entityType: this._entityName,\n name: '',\n };\n }\n\n getIsDirty(): boolean {\n for (const attr of this._attributes.values()) {\n if (attr.getIsDirty()) return true;\n }\n return false;\n }\n\n getPrimaryAttributeValue(): string {\n return '';\n }\n\n getDataXml(): string {\n return '';\n }\n\n attributes: Xrm.Collection.ItemCollection<Xrm.Attributes.Attribute> = {\n forEach: () => {},\n get: (() => null) as unknown as Xrm.Collection.ItemCollection<Xrm.Attributes.Attribute>['get'],\n getLength: () => 0,\n };\n\n save(): Xrm.Async.PromiseLike<void> {\n return { then: (cb: () => void) => cb() } as Xrm.Async.PromiseLike<void>;\n }\n}\n","/**\n * @xrmforge/testing - MockUi\n *\n * Mock implementation of Xrm.Ui.\n * Tracks form notifications for assertions.\n */\n\nexport interface FormNotification {\n message: string;\n level: string;\n}\n\nexport class MockUi {\n private _notifications: Map<string, FormNotification> = new Map();\n\n setFormNotification(message: string, level: string, uniqueId: string): boolean {\n this._notifications.set(uniqueId, { message, level });\n return true;\n }\n\n clearFormNotification(uniqueId: string): boolean {\n this._notifications.delete(uniqueId);\n return true;\n }\n\n /** Get a specific notification by ID (for assertions) */\n getFormNotification(uniqueId: string): FormNotification | undefined {\n return this._notifications.get(uniqueId);\n }\n\n /** Get all notifications (for assertions) */\n getFormNotifications(): ReadonlyMap<string, FormNotification> {\n return this._notifications;\n }\n\n /** Stub: tabs.get returns a minimal Tab mock */\n tabs = {\n get: (name: string): Xrm.Controls.Tab =>\n ({\n getName: () => name,\n getVisible: () => true,\n setVisible: () => {},\n getDisplayState: () => 'expanded' as Xrm.DisplayState,\n setDisplayState: () => {},\n getLabel: () => name,\n setLabel: () => {},\n setFocus: () => {},\n addTabStateChange: () => {},\n removeTabStateChange: () => {},\n getParent: () => ({}) as Xrm.Ui,\n sections: {\n forEach: () => {},\n get: ((sectionName: string) => ({\n getName: () => sectionName,\n getVisible: () => true,\n setVisible: () => {},\n getLabel: () => sectionName,\n setLabel: () => {},\n getParent: () => ({}) as Xrm.Controls.Tab,\n controls: { forEach: () => {}, get: (() => null) as any, getLength: () => 0 },\n })) as any,\n getLength: () => 0,\n },\n controls: { forEach: () => {}, get: (() => null) as any, getLength: () => 0 },\n }) as unknown as Xrm.Controls.Tab,\n forEach: () => {},\n getLength: () => 0,\n };\n\n close(): void {\n // no-op\n }\n\n getFormType(): XrmEnum.FormType {\n return 2 as XrmEnum.FormType; // Update\n }\n\n getViewPortHeight(): number {\n return 800;\n }\n\n getViewPortWidth(): number {\n return 1200;\n }\n\n refreshRibbon(_refreshAll?: boolean): void {\n // no-op\n }\n\n setFormEntityName(_name: string): void {\n // no-op\n }\n}\n","/**\n * @xrmforge/testing - MockEventContext\n *\n * Mock implementation of Xrm.Events.EventContext.\n * Used for onLoad/onSave/onChange handler invocation.\n */\n\nexport class MockEventContext {\n private _formContext: Xrm.FormContext;\n private _eventSource: unknown;\n\n constructor(formContext: Xrm.FormContext, eventSource?: unknown) {\n this._formContext = formContext;\n this._eventSource = eventSource ?? formContext;\n }\n\n getFormContext(): Xrm.FormContext {\n return this._formContext;\n }\n\n getEventSource(): unknown {\n return this._eventSource;\n }\n\n getContext(): Xrm.GlobalContext {\n // Minimal stub; tests that need specific GlobalContext values\n // should mock them explicitly via Object.assign or similar.\n return {\n getClientUrl: () => 'https://org.crm4.dynamics.com',\n getCurrentAppUrl: () => 'https://org.crm4.dynamics.com',\n client: {\n getClient: () => 'Web',\n getClientState: () => 'Online',\n isOffline: () => false,\n },\n userSettings: {\n userId: '{00000000-0000-0000-0000-000000000000}',\n userName: 'Test User',\n languageId: 1033,\n securityRoles: [],\n roles: { forEach: () => {}, get: (() => null) as never, getLength: () => 0 },\n getTimeZoneOffsetMinutes: () => 0,\n },\n getVersion: () => '9.2.0.0',\n isOnPremises: () => false,\n getOrgUniqueName: () => 'org',\n getOrgLcid: () => 1033,\n getUserLcid: () => 1033,\n } as unknown as Xrm.GlobalContext;\n }\n\n getDepth(): number {\n return 1;\n }\n\n getSharedVariable(_key: string): unknown {\n return undefined;\n }\n\n setSharedVariable(_key: string, _value: unknown): void {\n // no-op\n }\n}\n","/**\n * @xrmforge/testing - createFormMock\n *\n * Creates a type-safe mock FormContext from a simple values object.\n * The generated form interfaces provide compile-time field validation.\n *\n * @example\n * ```typescript\n * import { createFormMock } from '@xrmforge/testing';\n *\n * const mock = createFormMock<AccountMainForm>({\n * name: 'Contoso',\n * revenue: 150000,\n * });\n *\n * myOnLoad(mock.asEventContext());\n * expect(mock.getValue('name')).toBe('Contoso');\n * ```\n */\n\nimport type { CreateFormMockOptions, FormMock } from './types.js';\nimport { MockAttribute } from './mock-attribute.js';\nimport { MockControl } from './mock-control.js';\nimport { MockEntity } from './mock-entity.js';\nimport { MockUi } from './mock-ui.js';\nimport { MockEventContext } from './mock-event-context.js';\n\n/**\n * Create a type-safe mock FormContext for testing D365 form scripts.\n *\n * @typeParam TForm - Generated form interface (e.g. AccountMainForm)\n * @param values - Field values as a plain object (field name to value)\n * @param options - Optional entity ID and name\n * @returns FormMock with typed formContext and test accessors\n */\nexport function createFormMock<TForm>(\n values: Record<string, unknown> = {},\n options: CreateFormMockOptions = {},\n): FormMock<TForm> {\n const entityName = options.entityName ?? 'unknown';\n const entityId = options.entityId ?? '00000000-0000-0000-0000-000000000000';\n\n // Build attribute and control maps from values\n const attributes = new Map<string, MockAttribute>();\n const controls = new Map<string, MockControl>();\n\n for (const [name, value] of Object.entries(values)) {\n attributes.set(name, new MockAttribute(name, value));\n controls.set(name, new MockControl(name));\n }\n\n // Lazy-init: fields accessed but not in initial values get null\n const getOrCreateAttribute = (name: string): MockAttribute => {\n let attr = attributes.get(name);\n if (!attr) {\n attr = new MockAttribute(name, null);\n attributes.set(name, attr);\n }\n return attr;\n };\n\n const getOrCreateControl = (name: string): MockControl => {\n let ctrl = controls.get(name);\n if (!ctrl) {\n ctrl = new MockControl(name);\n controls.set(name, ctrl);\n }\n return ctrl;\n };\n\n const mockEntity = new MockEntity(entityName, entityId, attributes);\n const mockUi = new MockUi();\n\n // Build the formContext object that satisfies the TForm interface\n const formContext = {\n getAttribute: ((nameOrIndex?: string | number) => {\n if (typeof nameOrIndex === 'string') {\n return getOrCreateAttribute(nameOrIndex);\n }\n if (typeof nameOrIndex === 'number') {\n return [...attributes.values()][nameOrIndex] ?? null;\n }\n return [...attributes.values()];\n }) as Xrm.FormContext['getAttribute'],\n\n getControl: ((nameOrIndex?: string | number) => {\n if (typeof nameOrIndex === 'string') {\n return getOrCreateControl(nameOrIndex);\n }\n if (typeof nameOrIndex === 'number') {\n return [...controls.values()][nameOrIndex] ?? null;\n }\n return [...controls.values()];\n }) as Xrm.FormContext['getControl'],\n\n data: {\n entity: mockEntity as unknown as Xrm.Entity,\n process: {} as Xrm.ProcessFlow.ProcessManager,\n refresh: () =>\n ({ then: (cb: () => void) => cb() }) as Xrm.Async.PromiseLike<void>,\n save: () =>\n ({ then: (cb: () => void) => cb() }) as Xrm.Async.PromiseLike<void>,\n getIsDirty: () => mockEntity.getIsDirty(),\n isValid: () => true,\n attributes: {\n forEach: () => {},\n get: (() => null) as any,\n getLength: () => attributes.size,\n },\n },\n\n ui: mockUi as unknown as Xrm.Ui,\n } as unknown as TForm;\n\n return {\n formContext,\n\n getValue(name: string): unknown {\n return getOrCreateAttribute(name).getValue();\n },\n\n setValue(name: string, value: unknown): void {\n getOrCreateAttribute(name).setValue(value);\n },\n\n getAttribute(name: string): MockAttribute {\n return getOrCreateAttribute(name);\n },\n\n getControl(name: string): MockControl {\n return getOrCreateControl(name);\n },\n\n ui: mockUi,\n\n asEventContext(): Xrm.Events.EventContext {\n return new MockEventContext(\n formContext as unknown as Xrm.FormContext,\n ) as unknown as Xrm.Events.EventContext;\n },\n\n asAttributeEventContext(fieldName: string): Xrm.Events.EventContext {\n return new MockEventContext(\n formContext as unknown as Xrm.FormContext,\n getOrCreateAttribute(fieldName),\n ) as unknown as Xrm.Events.EventContext;\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/mock-attribute.ts","../src/mock-control.ts","../src/mock-entity.ts","../src/mock-ui.ts","../src/mock-event-context.ts","../src/create-form-mock.ts","../src/setup-xrm-mock.ts"],"sourcesContent":["/**\n * @xrmforge/testing - MockAttribute\n *\n * Mock implementation of Xrm.Attributes.Attribute.\n * Tracks value changes, required level, submit mode, and onChange handlers.\n */\n\ntype OnChangeHandler = (context: Xrm.Events.EventContext) => void;\n\nexport class MockAttribute {\n private _name: string;\n private _value: unknown;\n private _initialValue: unknown;\n private _requiredLevel: Xrm.Attributes.RequirementLevel = 'none';\n private _submitMode: Xrm.SubmitMode = 'dirty';\n private _onChangeHandlers: OnChangeHandler[] = [];\n\n constructor(name: string, value: unknown = null) {\n this._name = name;\n this._value = value;\n this._initialValue = value;\n }\n\n getName(): string {\n return this._name;\n }\n\n getValue(): unknown {\n return this._value;\n }\n\n setValue(value: unknown): void {\n this._value = value;\n }\n\n getIsDirty(): boolean {\n return this._value !== this._initialValue;\n }\n\n getRequiredLevel(): Xrm.Attributes.RequirementLevel {\n return this._requiredLevel;\n }\n\n setRequiredLevel(level: Xrm.Attributes.RequirementLevel): void {\n this._requiredLevel = level;\n }\n\n getSubmitMode(): Xrm.SubmitMode {\n return this._submitMode;\n }\n\n setSubmitMode(mode: Xrm.SubmitMode): void {\n this._submitMode = mode;\n }\n\n addOnChange(handler: OnChangeHandler): void {\n this._onChangeHandlers.push(handler);\n }\n\n removeOnChange(handler: OnChangeHandler): void {\n const index = this._onChangeHandlers.indexOf(handler);\n if (index >= 0) {\n this._onChangeHandlers.splice(index, 1);\n }\n }\n\n /** @internal Get registered onChange handlers (for testing/event simulation) */\n getOnChangeHandlers(): readonly OnChangeHandler[] {\n return this._onChangeHandlers;\n }\n\n /**\n * Fire all registered onChange handlers with the given event context.\n * @internal Called by FormMock.fireOnChange(fieldName)\n */\n fireOnChange(eventContext: Xrm.Events.EventContext): void {\n for (const handler of this._onChangeHandlers) {\n handler(eventContext);\n }\n }\n\n getAttributeType(): string {\n return 'string';\n }\n\n getFormat(): string | null {\n return null;\n }\n\n getParent(): Xrm.Entity {\n return {} as Xrm.Entity;\n }\n\n getUserPrivilege(): Xrm.Privilege {\n return { canRead: true, canUpdate: true, canCreate: true };\n }\n\n controls: Xrm.Collection.ItemCollection<Xrm.Controls.Control> = {\n forEach: () => {},\n get: (() => null) as unknown as Xrm.Collection.ItemCollection<Xrm.Controls.Control>['get'],\n getLength: () => 0,\n };\n}\n","/**\n * @xrmforge/testing - MockControl\n *\n * Mock implementation of Xrm.Controls.StandardControl.\n * Tracks visibility, disabled state, label, and notifications.\n */\n\nexport class MockControl {\n private _name: string;\n private _visible: boolean = true;\n private _disabled: boolean = false;\n private _label: string = '';\n private _notifications: Map<string, string> = new Map();\n\n constructor(name: string) {\n this._name = name;\n }\n\n getName(): string {\n return this._name;\n }\n\n getVisible(): boolean {\n return this._visible;\n }\n\n setVisible(visible: boolean): void {\n this._visible = visible;\n }\n\n getDisabled(): boolean {\n return this._disabled;\n }\n\n setDisabled(disabled: boolean): void {\n this._disabled = disabled;\n }\n\n getLabel(): string {\n return this._label;\n }\n\n setLabel(label: string): void {\n this._label = label;\n }\n\n setNotification(message: string, uniqueId?: string): boolean {\n this._notifications.set(uniqueId ?? '_default', message);\n return true;\n }\n\n clearNotification(uniqueId?: string): boolean {\n this._notifications.delete(uniqueId ?? '_default');\n return true;\n }\n\n /** @internal Get all notifications (for assertions) */\n getNotifications(): ReadonlyMap<string, string> {\n return this._notifications;\n }\n\n getControlType(): string {\n return 'standard';\n }\n\n getParent(): Xrm.Controls.Section {\n return {} as Xrm.Controls.Section;\n }\n\n /** Lookup-specific: addPreSearch (no-op for non-lookup controls) */\n addPreSearch(_handler: () => void): void {\n // no-op\n }\n\n /** Lookup-specific: addCustomFilter (no-op for non-lookup controls) */\n addCustomFilter(_filter: string, _entityLogicalName?: string): void {\n // no-op\n }\n}\n","/**\n * @xrmforge/testing - MockEntity\n *\n * Mock implementation of Xrm.Entity.\n */\n\nimport type { MockAttribute } from './mock-attribute.js';\n\nconst NULL_GUID = '00000000-0000-0000-0000-000000000000';\n\nexport class MockEntity {\n private _id: string;\n private _entityName: string;\n private _attributes: Map<string, MockAttribute>;\n\n constructor(\n entityName: string,\n entityId: string,\n attributes: Map<string, MockAttribute>,\n ) {\n this._entityName = entityName;\n this._id = entityId || NULL_GUID;\n this._attributes = attributes;\n }\n\n getId(): string {\n return `{${this._id}}`;\n }\n\n getEntityName(): string {\n return this._entityName;\n }\n\n getEntityReference(): Xrm.LookupValue {\n return {\n id: this._id,\n entityType: this._entityName,\n name: '',\n };\n }\n\n getIsDirty(): boolean {\n for (const attr of this._attributes.values()) {\n if (attr.getIsDirty()) return true;\n }\n return false;\n }\n\n getPrimaryAttributeValue(): string {\n return '';\n }\n\n getDataXml(): string {\n return '';\n }\n\n attributes: Xrm.Collection.ItemCollection<Xrm.Attributes.Attribute> = {\n forEach: () => {},\n get: (() => null) as unknown as Xrm.Collection.ItemCollection<Xrm.Attributes.Attribute>['get'],\n getLength: () => 0,\n };\n\n save(): Xrm.Async.PromiseLike<void> {\n return { then: (cb: () => void) => cb() } as Xrm.Async.PromiseLike<void>;\n }\n}\n","/**\n * @xrmforge/testing - MockUi\n *\n * Mock implementation of Xrm.Ui.\n * Tracks form notifications for assertions.\n */\n\nexport interface FormNotification {\n message: string;\n level: string;\n}\n\nexport class MockUi {\n private _notifications: Map<string, FormNotification> = new Map();\n\n setFormNotification(message: string, level: string, uniqueId: string): boolean {\n this._notifications.set(uniqueId, { message, level });\n return true;\n }\n\n clearFormNotification(uniqueId: string): boolean {\n this._notifications.delete(uniqueId);\n return true;\n }\n\n /** Get a specific notification by ID (for assertions) */\n getFormNotification(uniqueId: string): FormNotification | undefined {\n return this._notifications.get(uniqueId);\n }\n\n /** Get all notifications (for assertions) */\n getFormNotifications(): ReadonlyMap<string, FormNotification> {\n return this._notifications;\n }\n\n /** Stub: tabs.get returns a minimal Tab mock */\n tabs = {\n get: (name: string): Xrm.Controls.Tab =>\n ({\n getName: () => name,\n getVisible: () => true,\n setVisible: () => {},\n getDisplayState: () => 'expanded' as Xrm.DisplayState,\n setDisplayState: () => {},\n getLabel: () => name,\n setLabel: () => {},\n setFocus: () => {},\n addTabStateChange: () => {},\n removeTabStateChange: () => {},\n getParent: () => ({}) as Xrm.Ui,\n sections: {\n forEach: () => {},\n get: ((sectionName: string) => ({\n getName: () => sectionName,\n getVisible: () => true,\n setVisible: () => {},\n getLabel: () => sectionName,\n setLabel: () => {},\n getParent: () => ({}) as Xrm.Controls.Tab,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Xrm.Collection overloaded get()\n controls: { forEach: () => {}, get: (() => null) as any, getLength: () => 0 },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Xrm.Collection overloaded get()\n })) as any,\n getLength: () => 0,\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Xrm.Collection overloaded get()\n controls: { forEach: () => {}, get: (() => null) as any, getLength: () => 0 },\n }) as unknown as Xrm.Controls.Tab,\n forEach: () => {},\n getLength: () => 0,\n };\n\n close(): void {\n // no-op\n }\n\n getFormType(): XrmEnum.FormType {\n return 2 as XrmEnum.FormType; // Update\n }\n\n getViewPortHeight(): number {\n return 800;\n }\n\n getViewPortWidth(): number {\n return 1200;\n }\n\n refreshRibbon(_refreshAll?: boolean): void {\n // no-op\n }\n\n setFormEntityName(_name: string): void {\n // no-op\n }\n}\n","/**\n * @xrmforge/testing - MockEventContext\n *\n * Mock implementation of Xrm.Events.EventContext.\n * Used for onLoad/onSave/onChange handler invocation.\n */\n\nexport class MockEventContext {\n private _formContext: Xrm.FormContext;\n private _eventSource: unknown;\n\n constructor(formContext: Xrm.FormContext, eventSource?: unknown) {\n this._formContext = formContext;\n this._eventSource = eventSource ?? formContext;\n }\n\n getFormContext(): Xrm.FormContext {\n return this._formContext;\n }\n\n getEventSource(): unknown {\n return this._eventSource;\n }\n\n getContext(): Xrm.GlobalContext {\n // Minimal stub; tests that need specific GlobalContext values\n // should mock them explicitly via Object.assign or similar.\n return {\n getClientUrl: () => 'https://org.crm4.dynamics.com',\n getCurrentAppUrl: () => 'https://org.crm4.dynamics.com',\n client: {\n getClient: () => 'Web',\n getClientState: () => 'Online',\n isOffline: () => false,\n },\n userSettings: {\n userId: '{00000000-0000-0000-0000-000000000000}',\n userName: 'Test User',\n languageId: 1033,\n securityRoles: [],\n roles: { forEach: () => {}, get: (() => null) as never, getLength: () => 0 },\n getTimeZoneOffsetMinutes: () => 0,\n },\n getVersion: () => '9.2.0.0',\n isOnPremises: () => false,\n getOrgUniqueName: () => 'org',\n getOrgLcid: () => 1033,\n getUserLcid: () => 1033,\n } as unknown as Xrm.GlobalContext;\n }\n\n getDepth(): number {\n return 1;\n }\n\n getSharedVariable(_key: string): unknown {\n return undefined;\n }\n\n setSharedVariable(_key: string, _value: unknown): void {\n // no-op\n }\n}\n","/**\n * @xrmforge/testing - createFormMock\n *\n * Creates a type-safe mock FormContext from a simple values object.\n * The generated form interfaces provide compile-time field validation.\n *\n * @example\n * ```typescript\n * import { createFormMock } from '@xrmforge/testing';\n *\n * const mock = createFormMock<AccountMainForm>({\n * name: 'Contoso',\n * revenue: 150000,\n * });\n *\n * myOnLoad(mock.asEventContext());\n * expect(mock.getValue('name')).toBe('Contoso');\n * ```\n */\n\nimport type { CreateFormMockOptions, FormMock } from './types.js';\nimport { MockAttribute } from './mock-attribute.js';\nimport { MockControl } from './mock-control.js';\nimport { MockEntity } from './mock-entity.js';\nimport { MockUi } from './mock-ui.js';\nimport { MockEventContext } from './mock-event-context.js';\n\n/**\n * Create a type-safe mock FormContext for testing D365 form scripts.\n *\n * @typeParam TForm - Generated form interface (e.g. AccountMainForm)\n * @param values - Field values as a plain object (field name to value)\n * @param options - Optional entity ID and name\n * @returns FormMock with typed formContext and test accessors\n */\nexport function createFormMock<TForm>(\n values: Record<string, unknown> = {},\n options: CreateFormMockOptions = {},\n): FormMock<TForm> {\n const entityName = options.entityName ?? 'unknown';\n const entityId = options.entityId ?? '00000000-0000-0000-0000-000000000000';\n\n // Build attribute and control maps from values\n const attributes = new Map<string, MockAttribute>();\n const controls = new Map<string, MockControl>();\n\n for (const [name, value] of Object.entries(values)) {\n attributes.set(name, new MockAttribute(name, value));\n controls.set(name, new MockControl(name));\n }\n\n // Lazy-init: fields accessed but not in initial values get null\n const getOrCreateAttribute = (name: string): MockAttribute => {\n let attr = attributes.get(name);\n if (!attr) {\n attr = new MockAttribute(name, null);\n attributes.set(name, attr);\n }\n return attr;\n };\n\n const getOrCreateControl = (name: string): MockControl => {\n let ctrl = controls.get(name);\n if (!ctrl) {\n ctrl = new MockControl(name);\n controls.set(name, ctrl);\n }\n return ctrl;\n };\n\n const mockEntity = new MockEntity(entityName, entityId, attributes);\n const mockUi = new MockUi();\n\n // Build the formContext object that satisfies the TForm interface\n const formContext = {\n getAttribute: ((nameOrIndex?: string | number) => {\n if (typeof nameOrIndex === 'string') {\n return getOrCreateAttribute(nameOrIndex);\n }\n if (typeof nameOrIndex === 'number') {\n return [...attributes.values()][nameOrIndex] ?? null;\n }\n return [...attributes.values()];\n }) as Xrm.FormContext['getAttribute'],\n\n getControl: ((nameOrIndex?: string | number) => {\n if (typeof nameOrIndex === 'string') {\n return getOrCreateControl(nameOrIndex);\n }\n if (typeof nameOrIndex === 'number') {\n return [...controls.values()][nameOrIndex] ?? null;\n }\n return [...controls.values()];\n }) as Xrm.FormContext['getControl'],\n\n data: {\n entity: mockEntity as unknown as Xrm.Entity,\n process: {} as Xrm.ProcessFlow.ProcessManager,\n refresh: () =>\n ({ then: (cb: () => void) => cb() }) as Xrm.Async.PromiseLike<void>,\n save: () =>\n ({ then: (cb: () => void) => cb() }) as Xrm.Async.PromiseLike<void>,\n getIsDirty: () => mockEntity.getIsDirty(),\n isValid: () => true,\n attributes: {\n forEach: () => {},\n // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Xrm.Collection.ItemCollection has complex overloaded get()\n get: (() => null) as any,\n getLength: () => attributes.size,\n },\n },\n\n ui: mockUi as unknown as Xrm.Ui,\n } as unknown as TForm;\n\n return {\n formContext,\n\n getValue(name: string): unknown {\n return getOrCreateAttribute(name).getValue();\n },\n\n setValue(name: string, value: unknown): void {\n getOrCreateAttribute(name).setValue(value);\n },\n\n getAttribute(name: string): MockAttribute {\n return getOrCreateAttribute(name);\n },\n\n getControl(name: string): MockControl {\n return getOrCreateControl(name);\n },\n\n ui: mockUi,\n\n asEventContext(): Xrm.Events.EventContext {\n return new MockEventContext(\n formContext as unknown as Xrm.FormContext,\n ) as unknown as Xrm.Events.EventContext;\n },\n\n asAttributeEventContext(fieldName: string): Xrm.Events.EventContext {\n return new MockEventContext(\n formContext as unknown as Xrm.FormContext,\n getOrCreateAttribute(fieldName),\n ) as unknown as Xrm.Events.EventContext;\n },\n\n fireOnChange(fieldName: string): void {\n const attr = getOrCreateAttribute(fieldName);\n const eventContext = new MockEventContext(\n formContext as unknown as Xrm.FormContext,\n attr,\n ) as unknown as Xrm.Events.EventContext;\n attr.fireOnChange(eventContext);\n },\n };\n}\n","/**\n * @xrmforge/testing - Global Xrm Mock Setup\n *\n * Sets up a minimal global Xrm object for testing form scripts that\n * call Xrm.WebApi, Xrm.Navigation, or Xrm.Utility.\n *\n * Usage in vitest setup or beforeEach:\n * ```typescript\n * import { setupXrmMock, teardownXrmMock } from '@xrmforge/testing';\n *\n * beforeEach(() => setupXrmMock());\n * afterEach(() => teardownXrmMock());\n * ```\n *\n * All WebApi methods return resolved promises with empty results by default.\n * Override specific methods in your test:\n * ```typescript\n * setupXrmMock({\n * webApiOverrides: {\n * retrieveMultipleRecords: async () => ({ entities: [{ name: 'Test' }] }),\n * },\n * });\n * ```\n */\n\n/** Options for customizing the Xrm mock */\nexport interface SetupXrmMockOptions {\n /** Override specific Xrm.WebApi methods */\n webApiOverrides?: Partial<{\n retrieveRecord: (entityType: string, id: string, options?: string) => Promise<Record<string, unknown>>;\n retrieveMultipleRecords: (entityType: string, options?: string) => Promise<{ entities: Record<string, unknown>[] }>;\n createRecord: (entityType: string, data: Record<string, unknown>) => Promise<{ id: string }>;\n updateRecord: (entityType: string, id: string, data: Record<string, unknown>) => Promise<{ id: string }>;\n deleteRecord: (entityType: string, id: string) => Promise<{ id: string }>;\n }>;\n}\n\n/**\n * Set up a global Xrm mock for testing.\n * Call in beforeEach() or vitest setup file.\n */\nexport function setupXrmMock(options?: SetupXrmMockOptions): void {\n const webApi = {\n retrieveRecord: options?.webApiOverrides?.retrieveRecord\n ?? (async () => ({})),\n retrieveMultipleRecords: options?.webApiOverrides?.retrieveMultipleRecords\n ?? (async () => ({ entities: [] })),\n createRecord: options?.webApiOverrides?.createRecord\n ?? (async () => ({ id: '00000000-0000-0000-0000-000000000000' })),\n updateRecord: options?.webApiOverrides?.updateRecord\n ?? (async () => ({ id: '00000000-0000-0000-0000-000000000000' })),\n deleteRecord: options?.webApiOverrides?.deleteRecord\n ?? (async () => ({ id: '00000000-0000-0000-0000-000000000000' })),\n };\n\n const navigation = {\n openAlertDialog: async () => ({}),\n openConfirmDialog: async () => ({ confirmed: false }),\n openForm: async () => ({ savedEntityReference: [] }),\n openFile: async () => undefined,\n };\n\n const utility = {\n showProgressIndicator: () => undefined,\n closeProgressIndicator: () => undefined,\n getGlobalContext: () => ({\n getClientUrl: () => 'https://test.crm4.dynamics.com',\n organizationSettings: { uniqueName: 'testorg' },\n userSettings: {\n userId: '{00000000-0000-0000-0000-000000000001}',\n userName: 'Test User',\n languageId: 1033,\n },\n }),\n };\n\n const xrmMock = {\n WebApi: webApi,\n Navigation: navigation,\n Utility: utility,\n };\n\n (globalThis as Record<string, unknown>)['Xrm'] = xrmMock;\n}\n\n/**\n * Remove the global Xrm mock.\n * Call in afterEach() to clean up.\n */\nexport function teardownXrmMock(): void {\n delete (globalThis as Record<string, unknown>)['Xrm'];\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xrmforge/testing",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Type-safe testing utilities for Dynamics 365 form scripts",
5
5
  "keywords": [
6
6
  "dynamics-365",
@@ -36,17 +36,9 @@
36
36
  "dist"
37
37
  ],
38
38
  "sideEffects": false,
39
- "scripts": {
40
- "build": "tsup",
41
- "dev": "tsup --watch",
42
- "test": "vitest run",
43
- "test:watch": "vitest",
44
- "typecheck": "tsc --noEmit",
45
- "lint": "eslint src/",
46
- "clean": "rm -rf dist"
47
- },
48
39
  "devDependencies": {
49
40
  "@types/node": "^22.0.0",
41
+ "@vitest/coverage-v8": "^3.2.4",
50
42
  "tsup": "^8.3.0",
51
43
  "typescript": "^5.7.0",
52
44
  "vitest": "^3.0.0"
@@ -56,5 +48,14 @@
56
48
  },
57
49
  "engines": {
58
50
  "node": ">=20.0.0"
51
+ },
52
+ "scripts": {
53
+ "build": "tsup",
54
+ "dev": "tsup --watch",
55
+ "test": "vitest run",
56
+ "test:watch": "vitest",
57
+ "typecheck": "tsc --noEmit",
58
+ "lint": "eslint src/",
59
+ "clean": "rm -rf dist"
59
60
  }
60
- }
61
+ }