@memberjunction/ng-artifacts 5.43.0 → 5.45.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.
Files changed (65) hide show
  1. package/README.md +6 -0
  2. package/dist/lib/artifacts.module.d.ts +28 -25
  3. package/dist/lib/artifacts.module.d.ts.map +1 -1
  4. package/dist/lib/artifacts.module.js +22 -2
  5. package/dist/lib/artifacts.module.js.map +1 -1
  6. package/dist/lib/components/artifact-version-history.component.d.ts +1 -0
  7. package/dist/lib/components/artifact-version-history.component.d.ts.map +1 -1
  8. package/dist/lib/components/artifact-version-history.component.js +30 -31
  9. package/dist/lib/components/artifact-version-history.component.js.map +1 -1
  10. package/dist/lib/components/artifact-viewer-panel.component.d.ts +3 -0
  11. package/dist/lib/components/artifact-viewer-panel.component.d.ts.map +1 -1
  12. package/dist/lib/components/artifact-viewer-panel.component.js +198 -112
  13. package/dist/lib/components/artifact-viewer-panel.component.js.map +1 -1
  14. package/dist/lib/components/plugins/audio-artifact-viewer.component.d.ts +11 -0
  15. package/dist/lib/components/plugins/audio-artifact-viewer.component.d.ts.map +1 -1
  16. package/dist/lib/components/plugins/audio-artifact-viewer.component.js +37 -12
  17. package/dist/lib/components/plugins/audio-artifact-viewer.component.js.map +1 -1
  18. package/dist/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.d.ts.map +1 -1
  19. package/dist/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.js +40 -51
  20. package/dist/lib/components/plugins/component-feedback-panel/component-feedback-panel.component.js.map +1 -1
  21. package/dist/lib/components/plugins/data-artifact-viewer.component.js +27 -47
  22. package/dist/lib/components/plugins/data-artifact-viewer.component.js.map +1 -1
  23. package/dist/lib/components/plugins/data-requirements-viewer/data-requirements-viewer.component.js +89 -95
  24. package/dist/lib/components/plugins/data-requirements-viewer/data-requirements-viewer.component.js.map +1 -1
  25. package/dist/lib/components/plugins/ml-experiment-results-viewer.component.d.ts +227 -0
  26. package/dist/lib/components/plugins/ml-experiment-results-viewer.component.d.ts.map +1 -0
  27. package/dist/lib/components/plugins/ml-experiment-results-viewer.component.js +630 -0
  28. package/dist/lib/components/plugins/ml-experiment-results-viewer.component.js.map +1 -0
  29. package/dist/lib/components/plugins/video-artifact-viewer.component.d.ts +11 -0
  30. package/dist/lib/components/plugins/video-artifact-viewer.component.d.ts.map +1 -1
  31. package/dist/lib/components/plugins/video-artifact-viewer.component.js +37 -12
  32. package/dist/lib/components/plugins/video-artifact-viewer.component.js.map +1 -1
  33. package/dist/lib/components/previews/audio-artifact-preview.component.d.ts +18 -4
  34. package/dist/lib/components/previews/audio-artifact-preview.component.d.ts.map +1 -1
  35. package/dist/lib/components/previews/audio-artifact-preview.component.js +54 -21
  36. package/dist/lib/components/previews/audio-artifact-preview.component.js.map +1 -1
  37. package/dist/lib/components/previews/video-artifact-preview.component.d.ts +19 -5
  38. package/dist/lib/components/previews/video-artifact-preview.component.d.ts.map +1 -1
  39. package/dist/lib/components/previews/video-artifact-preview.component.js +54 -22
  40. package/dist/lib/components/previews/video-artifact-preview.component.js.map +1 -1
  41. package/dist/public-api.d.ts +1 -0
  42. package/dist/public-api.d.ts.map +1 -1
  43. package/dist/public-api.js +1 -0
  44. package/dist/public-api.js.map +1 -1
  45. package/package.json +21 -18
  46. package/dist/__tests__/exports.test.d.ts +0 -2
  47. package/dist/__tests__/exports.test.d.ts.map +0 -1
  48. package/dist/__tests__/exports.test.js +0 -17
  49. package/dist/__tests__/exports.test.js.map +0 -1
  50. package/dist/__tests__/image-artifact-viewer.test.d.ts +0 -2
  51. package/dist/__tests__/image-artifact-viewer.test.d.ts.map +0 -1
  52. package/dist/__tests__/image-artifact-viewer.test.js +0 -62
  53. package/dist/__tests__/image-artifact-viewer.test.js.map +0 -1
  54. package/dist/__tests__/index.test.d.ts +0 -2
  55. package/dist/__tests__/index.test.d.ts.map +0 -1
  56. package/dist/__tests__/index.test.js +0 -7
  57. package/dist/__tests__/index.test.js.map +0 -1
  58. package/dist/lib/__tests__/artifact-snapshots.test.d.ts +0 -2
  59. package/dist/lib/__tests__/artifact-snapshots.test.d.ts.map +0 -1
  60. package/dist/lib/__tests__/artifact-snapshots.test.js +0 -155
  61. package/dist/lib/__tests__/artifact-snapshots.test.js.map +0 -1
  62. package/dist/lib/__tests__/interactive-form-apply.service.test.d.ts +0 -2
  63. package/dist/lib/__tests__/interactive-form-apply.service.test.d.ts.map +0 -1
  64. package/dist/lib/__tests__/interactive-form-apply.service.test.js +0 -211
  65. package/dist/lib/__tests__/interactive-form-apply.service.test.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"artifact-snapshots.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/artifact-snapshots.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect } from 'vitest';\nimport { DataSnapshot, DataTable, NormalizeToTables } from '@memberjunction/core';\nimport {\n createJsonSnapshot,\n createCodeSnapshot,\n createMarkdownSnapshot,\n createDataSnapshot,\n buildMultiTableSql,\n} from '../snapshot-helpers';\n\n/**\n * Tests for artifact-level state snapshot logic.\n *\n * These tests exercise the pure snapshot-creation functions that the Angular\n * viewer components delegate to, without requiring Angular TestBed.\n */\n\ndescribe('Artifact Snapshot Helpers', () => {\n\n describe('createJsonSnapshot', () => {\n it('should return null when content is null', () => {\n const result = createJsonSnapshot(null, 'Test');\n expect(result).toBeNull();\n });\n\n it('should parse valid JSON and count top-level keys', () => {\n const json = '{\"name\":\"Alice\",\"age\":30,\"active\":true}';\n const result = createJsonSnapshot(json, 'My JSON');\n expect(result).not.toBeNull();\n expect(result!.title).toBe('My JSON');\n expect(result!.interpretation).toBe('JSON artifact with 3 top-level keys.');\n expect(result!.custom).toEqual({ name: 'Alice', age: 30, active: true });\n });\n\n it('should return null for invalid JSON', () => {\n const result = createJsonSnapshot('not json {{{', 'Bad');\n expect(result).toBeNull();\n });\n\n it('should handle JSON arrays', () => {\n const json = '[1,2,3]';\n const result = createJsonSnapshot(json, 'Array');\n expect(result).not.toBeNull();\n // Arrays don't have \"top-level keys\" — no interpretation set\n expect(result!.interpretation).toBeUndefined();\n expect(result!.custom).toEqual([1, 2, 3]);\n });\n });\n\n describe('createCodeSnapshot', () => {\n it('should return null when content is null', () => {\n const result = createCodeSnapshot(null, 'Test');\n expect(result).toBeNull();\n });\n\n it('should count lines and include content in custom', () => {\n const code = 'line1\\nline2\\nline3';\n const result = createCodeSnapshot(code, 'My Code');\n expect(result).not.toBeNull();\n expect(result!.title).toBe('My Code');\n expect(result!.interpretation).toBe('Code artifact, 3 lines.');\n expect(result!.custom).toEqual({ content: code, lineCount: 3 });\n });\n\n it('should handle single line content', () => {\n const result = createCodeSnapshot('console.log(\"hi\")', 'One Liner');\n expect(result!.interpretation).toBe('Code artifact, 1 lines.');\n expect((result!.custom as { lineCount: number }).lineCount).toBe(1);\n });\n });\n\n describe('createMarkdownSnapshot', () => {\n it('should return null when content is null', () => {\n const result = createMarkdownSnapshot(null, 'Test');\n expect(result).toBeNull();\n });\n\n it('should include content in custom', () => {\n const md = '# Hello\\n\\nWorld';\n const result = createMarkdownSnapshot(md, 'My Doc');\n expect(result).not.toBeNull();\n expect(result!.title).toBe('My Doc');\n expect(result!.custom).toEqual({ content: md });\n });\n });\n\n describe('createDataSnapshot', () => {\n it('should return null when spec is null', () => {\n const result = createDataSnapshot(null);\n expect(result).toBeNull();\n });\n\n it('should normalize a spec with columns and rows into tables', () => {\n const spec = {\n title: 'Sales Report',\n columns: [\n { field: 'Name', headerName: 'Customer Name' },\n { field: 'Amount', headerName: 'Total', sqlBaseType: 'money' },\n ],\n rows: [\n { Name: 'Alice', Amount: 100 },\n { Name: 'Bob', Amount: 200 },\n ],\n };\n const result = createDataSnapshot(spec);\n expect(result).not.toBeNull();\n expect(result!.title).toBe('Sales Report');\n expect(result!.tables).toBeDefined();\n expect(result!.tables!.length).toBeGreaterThanOrEqual(1);\n expect(result!.tables![0].rows).toHaveLength(2);\n });\n\n it('should use \"Results\" as default table name when title is missing', () => {\n const spec = {\n columns: [{ field: 'ID' }],\n rows: [{ ID: 1 }],\n };\n const result = createDataSnapshot(spec);\n expect(result).not.toBeNull();\n expect(result!.tables).toBeDefined();\n expect(result!.tables![0].name).toBe('Results');\n });\n });\n});\n\ndescribe('buildMultiTableSql', () => {\n it('concatenates SQL from multiple tables with headers', () => {\n const tables = [\n { name: 'customers', metadata: { sql: 'SELECT * FROM Customers' } },\n { name: 'orders', metadata: { sql: 'SELECT * FROM Orders' } },\n ];\n const result = buildMultiTableSql(tables as DataTable[]);\n\n expect(result).toContain('-- ═══ customers ═══');\n expect(result).toContain('SELECT * FROM Customers');\n expect(result).toContain('-- ═══ orders ═══');\n expect(result).toContain('SELECT * FROM Orders');\n });\n\n it('skips tables without SQL', () => {\n const tables = [\n { name: 'has_sql', metadata: { sql: 'SELECT 1' } },\n { name: 'no_sql', metadata: {} },\n { name: 'no_meta' },\n ];\n const result = buildMultiTableSql(tables as DataTable[]);\n\n expect(result).toContain('-- ═══ has_sql ═══');\n expect(result).not.toContain('no_sql');\n expect(result).not.toContain('no_meta');\n });\n\n it('returns null when no tables have SQL', () => {\n const tables = [{ name: 'a' }, { name: 'b', metadata: {} }];\n const result = buildMultiTableSql(tables as DataTable[]);\n expect(result).toBeNull();\n });\n});\n\ndescribe('NormalizeToTables (core utility)', () => {\n it('should return empty array for empty snapshot', () => {\n const snap = new DataSnapshot();\n expect(NormalizeToTables(snap)).toEqual([]);\n });\n\n it('should handle a spec with columns and rows (legacy format)', () => {\n const data = {\n columns: [{ field: 'A' }, { field: 'B' }],\n rows: [{ A: 1, B: 2 }],\n };\n const tables = NormalizeToTables(data, 'Sheet1');\n expect(tables).toHaveLength(1);\n expect(tables[0].rows).toHaveLength(1);\n });\n\n it('should return empty for a context-only snapshot', () => {\n const snap = new DataSnapshot();\n snap.title = 'Just a title';\n const tables = NormalizeToTables(snap);\n expect(tables).toEqual([]);\n });\n});\n"]}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=interactive-form-apply.service.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"interactive-form-apply.service.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/interactive-form-apply.service.test.ts"],"names":[],"mappings":""}
@@ -1,211 +0,0 @@
1
- /**
2
- * End-to-end-ish coverage for the Apply-to-my-form flow.
3
- *
4
- * The service is the join point that closes the chat-artifact → action loop:
5
- * form-aware artifact viewer → applyFormRequested →
6
- * conversation-chat-area / artifact-resource → ConfirmAndApply →
7
- * Get Active Form For Entity (action) → Create or Modify (action)
8
- *
9
- * These tests mock the dialog, notifications, and the GraphQL action
10
- * client so we can drive both branches (no-existing → Create; existing →
11
- * Modify) and the cancellation path, plus the failure surfaces. Without
12
- * this suite the original "Apply button is wired to nothing" bug would
13
- * regress silently — the unit tests on the actions wouldn't notice.
14
- */
15
- import { describe, it, expect, vi, beforeEach } from 'vitest';
16
- // ─── Hoisted state buckets the mocks read/write ──────────────────────────
17
- const hoisted = vi.hoisted(() => ({
18
- dialogResult: 'apply',
19
- actionResponses: new Map(),
20
- actionCalls: [],
21
- runViewResponses: [],
22
- notifications: [],
23
- }));
24
- // ─── Module mocks ────────────────────────────────────────────────────────
25
- vi.mock('@angular/core', () => ({
26
- Injectable: () => (target) => target,
27
- inject: (token) => {
28
- // Return the mock singletons by class identity.
29
- const name = token.name ?? '';
30
- if (name === 'MJDialogService')
31
- return mockDialog;
32
- if (name === 'MJNotificationService')
33
- return mockNotifications;
34
- return {};
35
- },
36
- }));
37
- const mockDialog = {
38
- Open: () => ({
39
- // The real MJDialogRef.Result emits the clicked MJDialogAction object
40
- // ({ text, primary }) — the service detects intent via `action.primary`.
41
- // Map the test's 'apply'/'cancel' intent onto that shape.
42
- Result: {
43
- subscribe: (cb) => {
44
- cb(hoisted.dialogResult === 'apply'
45
- ? { text: 'Apply', primary: true }
46
- : { text: 'Cancel' });
47
- },
48
- },
49
- }),
50
- };
51
- const mockNotifications = {
52
- CreateSimpleNotification: (message, type) => {
53
- hoisted.notifications.push({ message, type });
54
- },
55
- };
56
- vi.mock('@memberjunction/ng-ui-components', () => ({ MJDialogService: class MJDialogService {
57
- } }));
58
- vi.mock('@memberjunction/ng-notifications', () => ({ MJNotificationService: class MJNotificationService {
59
- } }));
60
- vi.mock('@memberjunction/graphql-dataprovider', () => ({
61
- GraphQLActionClient: class {
62
- async RunAction(actionId, params) {
63
- hoisted.actionCalls.push({ id: actionId, params });
64
- return hoisted.actionResponses.get(actionId) ?? { Success: false, Message: 'mock: no response set' };
65
- }
66
- },
67
- GraphQLDataProvider: class {
68
- },
69
- }));
70
- vi.mock('@memberjunction/core', async () => {
71
- const actual = await vi.importActual('@memberjunction/core');
72
- return {
73
- ...actual,
74
- Metadata: { Provider: null },
75
- LogError: vi.fn(),
76
- RunView: class {
77
- async RunView() {
78
- return hoisted.runViewResponses.shift() ?? { Success: true, Results: [] };
79
- }
80
- },
81
- };
82
- });
83
- // ─── Test setup ──────────────────────────────────────────────────────────
84
- import { InteractiveFormApplyService } from '../services/interactive-form-apply.service';
85
- function mockProvider(overrides = {}) {
86
- return {
87
- EntityByName: () => ({ ID: 'ENT-1', Name: 'MJ: Apps' }),
88
- CurrentUser: { ID: 'U1', Name: 'Test' },
89
- ExecuteGQL: () => Promise.resolve({}), // marks it as a GraphQL provider
90
- ...overrides,
91
- };
92
- }
93
- function spec(over = {}) {
94
- return { name: 'Form', componentRole: 'form', location: 'embedded', code: '() => null', ...over };
95
- }
96
- beforeEach(() => {
97
- hoisted.dialogResult = 'apply';
98
- hoisted.actionResponses.clear();
99
- hoisted.actionCalls.length = 0;
100
- hoisted.runViewResponses.length = 0;
101
- hoisted.notifications.length = 0;
102
- });
103
- describe('InteractiveFormApplyService', () => {
104
- it('returns failure when no metadata provider is available', async () => {
105
- const svc = new InteractiveFormApplyService();
106
- // No provider — neither passed nor on Metadata.Provider.
107
- const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps');
108
- expect(result.Success).toBe(false);
109
- expect(result.Message).toMatch(/No metadata provider/);
110
- });
111
- it('returns failure when the declared entity is not registered', async () => {
112
- const svc = new InteractiveFormApplyService();
113
- const provider = mockProvider({ EntityByName: () => undefined });
114
- const result = await svc.ConfirmAndApply(spec(), 'MJ: NotAnEntity', provider);
115
- expect(result.Success).toBe(false);
116
- expect(result.Message).toMatch(/not registered/);
117
- });
118
- it('cancellation by user yields a friendly failure result, no action invoked', async () => {
119
- hoisted.dialogResult = 'cancel';
120
- // Seed action-id lookup so the path past dialog is reachable in case the
121
- // implementation order changes; with dialog returning 'cancel' the
122
- // Create action should never be called.
123
- hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });
124
- hoisted.actionResponses.set('ACT-GET-ACTIVE', {
125
- Success: true,
126
- Message: JSON.stringify({ Active: null, Variants: [] }),
127
- });
128
- const svc = new InteractiveFormApplyService();
129
- const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps', mockProvider());
130
- expect(result.Success).toBe(false);
131
- expect(result.Message).toMatch(/Cancelled/);
132
- // Only Get Active Form should have been called (to populate dialog text);
133
- // Create / Modify should NOT have been called.
134
- const calls = hoisted.actionCalls.map(c => c.id);
135
- expect(calls.filter(id => id !== 'ACT-GET-ACTIVE')).toEqual([]);
136
- });
137
- it('no existing override → calls Create Interactive Form', async () => {
138
- // RunView lookups: each call resolves the next action ID.
139
- hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });
140
- hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-CREATE' }] });
141
- hoisted.actionResponses.set('ACT-GET-ACTIVE', {
142
- Success: true,
143
- Message: JSON.stringify({ Active: null, Variants: [] }),
144
- });
145
- hoisted.actionResponses.set('ACT-CREATE', {
146
- Success: true,
147
- Message: JSON.stringify({ ComponentID: 'NEW-COMP', OverrideID: 'NEW-OVER', Version: '1.0.0' }),
148
- });
149
- const svc = new InteractiveFormApplyService();
150
- const result = await svc.ConfirmAndApply(spec({ name: 'CompactForm' }), 'MJ: Apps', mockProvider());
151
- expect(result.Success).toBe(true);
152
- expect(result.Mode).toBe('create');
153
- expect(result.OverrideID).toBe('NEW-OVER');
154
- expect(result.Version).toBe('1.0.0');
155
- const calls = hoisted.actionCalls.map(c => c.id);
156
- expect(calls).toEqual(['ACT-GET-ACTIVE', 'ACT-CREATE']);
157
- // Success notification surfaced.
158
- expect(hoisted.notifications.some(n => n.type === 'success')).toBe(true);
159
- });
160
- it('existing Active override → calls Modify Interactive Form (Pending sibling produced)', async () => {
161
- hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });
162
- hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-MODIFY' }] });
163
- hoisted.actionResponses.set('ACT-GET-ACTIVE', {
164
- Success: true,
165
- Message: JSON.stringify({
166
- Active: { OverrideID: 'OVER-EXISTING', ComponentID: 'COMP-EXISTING', ComponentVersion: '1.0.0' },
167
- Variants: [],
168
- }),
169
- });
170
- hoisted.actionResponses.set('ACT-MODIFY', {
171
- Success: true,
172
- Message: JSON.stringify({ Mode: 'new-version', ComponentID: 'NEW-COMP', OverrideID: 'NEW-OVER', Version: '1.1.0' }),
173
- });
174
- const svc = new InteractiveFormApplyService();
175
- const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps', mockProvider());
176
- expect(result.Success).toBe(true);
177
- expect(result.Mode).toBe('modify-new-version');
178
- expect(result.Version).toBe('1.1.0');
179
- const calls = hoisted.actionCalls.map(c => c.id);
180
- expect(calls).toEqual(['ACT-GET-ACTIVE', 'ACT-MODIFY']);
181
- });
182
- it('Get Active failure short-circuits with an error', async () => {
183
- hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });
184
- hoisted.actionResponses.set('ACT-GET-ACTIVE', {
185
- Success: false,
186
- Message: 'DB unavailable',
187
- });
188
- const svc = new InteractiveFormApplyService();
189
- const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps', mockProvider());
190
- expect(result.Success).toBe(false);
191
- expect(result.Message).toMatch(/Could not check for existing override/);
192
- });
193
- it('failure on the Create action surfaces the error notification', async () => {
194
- hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });
195
- hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-CREATE' }] });
196
- hoisted.actionResponses.set('ACT-GET-ACTIVE', {
197
- Success: true,
198
- Message: JSON.stringify({ Active: null, Variants: [] }),
199
- });
200
- hoisted.actionResponses.set('ACT-CREATE', {
201
- Success: false,
202
- ResultCode: 'LINT_FAILED',
203
- Message: 'Spec failed linting.',
204
- });
205
- const svc = new InteractiveFormApplyService();
206
- const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps', mockProvider());
207
- expect(result.Success).toBe(false);
208
- expect(hoisted.notifications.some(n => n.type === 'error')).toBe(true);
209
- });
210
- });
211
- //# sourceMappingURL=interactive-form-apply.service.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"interactive-form-apply.service.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/interactive-form-apply.service.test.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * End-to-end-ish coverage for the Apply-to-my-form flow.\n *\n * The service is the join point that closes the chat-artifact → action loop:\n * form-aware artifact viewer → applyFormRequested →\n * conversation-chat-area / artifact-resource → ConfirmAndApply →\n * Get Active Form For Entity (action) → Create or Modify (action)\n *\n * These tests mock the dialog, notifications, and the GraphQL action\n * client so we can drive both branches (no-existing → Create; existing →\n * Modify) and the cancellation path, plus the failure surfaces. Without\n * this suite the original \"Apply button is wired to nothing\" bug would\n * regress silently — the unit tests on the actions wouldn't notice.\n */\nimport { describe, it, expect, vi, beforeEach } from 'vitest';\nimport type { ComponentSpec } from '@memberjunction/interactive-component-types';\n\n// ─── Hoisted state buckets the mocks read/write ──────────────────────────\n\nconst hoisted = vi.hoisted(() => ({\n dialogResult: 'apply' as 'apply' | 'cancel',\n actionResponses: new Map<string, { Success: boolean; Message?: string; ResultCode?: string }>(),\n actionCalls: [] as Array<{ id: string; params: unknown }>,\n runViewResponses: [] as Array<{ Success: boolean; Results: Array<{ ID: string }>; ErrorMessage?: string }>,\n notifications: [] as Array<{ message: string; type: string }>,\n}));\n\n// ─── Module mocks ────────────────────────────────────────────────────────\n\nvi.mock('@angular/core', () => ({\n Injectable: () => (target: Function) => target,\n inject: (token: { Instance?: unknown } | typeof Object) => {\n // Return the mock singletons by class identity.\n const name = (token as { name?: string }).name ?? '';\n if (name === 'MJDialogService') return mockDialog;\n if (name === 'MJNotificationService') return mockNotifications;\n return {};\n },\n}));\n\nconst mockDialog = {\n Open: () => ({\n // The real MJDialogRef.Result emits the clicked MJDialogAction object\n // ({ text, primary }) — the service detects intent via `action.primary`.\n // Map the test's 'apply'/'cancel' intent onto that shape.\n Result: {\n subscribe: (cb: (r: { text: string; primary?: boolean }) => void) => {\n cb(\n hoisted.dialogResult === 'apply'\n ? { text: 'Apply', primary: true }\n : { text: 'Cancel' },\n );\n },\n },\n }),\n};\n\nconst mockNotifications = {\n CreateSimpleNotification: (message: string, type: string) => {\n hoisted.notifications.push({ message, type });\n },\n};\n\nvi.mock('@memberjunction/ng-ui-components', () => ({ MJDialogService: class MJDialogService {} }));\nvi.mock('@memberjunction/ng-notifications', () => ({ MJNotificationService: class MJNotificationService {} }));\n\nvi.mock('@memberjunction/graphql-dataprovider', () => ({\n GraphQLActionClient: class {\n async RunAction(actionId: string, params: unknown) {\n hoisted.actionCalls.push({ id: actionId, params });\n return hoisted.actionResponses.get(actionId) ?? { Success: false, Message: 'mock: no response set' };\n }\n },\n GraphQLDataProvider: class {},\n}));\n\nvi.mock('@memberjunction/core', async () => {\n const actual = await vi.importActual<Record<string, unknown>>('@memberjunction/core');\n return {\n ...actual,\n Metadata: { Provider: null },\n LogError: vi.fn(),\n RunView: class {\n async RunView() {\n return hoisted.runViewResponses.shift() ?? { Success: true, Results: [] };\n }\n },\n };\n});\n\n// ─── Test setup ──────────────────────────────────────────────────────────\n\nimport { InteractiveFormApplyService } from '../services/interactive-form-apply.service';\n\nfunction mockProvider(overrides: Partial<{ EntityByName: () => unknown; CurrentUser: unknown }> = {}) {\n return {\n EntityByName: () => ({ ID: 'ENT-1', Name: 'MJ: Apps' }),\n CurrentUser: { ID: 'U1', Name: 'Test' },\n ExecuteGQL: () => Promise.resolve({}), // marks it as a GraphQL provider\n ...overrides,\n } as unknown as Parameters<InteractiveFormApplyService['ConfirmAndApply']>[2];\n}\n\nfunction spec(over: Partial<ComponentSpec> = {}): ComponentSpec {\n return { name: 'Form', componentRole: 'form', location: 'embedded', code: '() => null', ...over } as ComponentSpec;\n}\n\nbeforeEach(() => {\n hoisted.dialogResult = 'apply';\n hoisted.actionResponses.clear();\n hoisted.actionCalls.length = 0;\n hoisted.runViewResponses.length = 0;\n hoisted.notifications.length = 0;\n});\n\ndescribe('InteractiveFormApplyService', () => {\n\n it('returns failure when no metadata provider is available', async () => {\n const svc = new InteractiveFormApplyService();\n // No provider — neither passed nor on Metadata.Provider.\n const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps');\n expect(result.Success).toBe(false);\n expect(result.Message).toMatch(/No metadata provider/);\n });\n\n it('returns failure when the declared entity is not registered', async () => {\n const svc = new InteractiveFormApplyService();\n const provider = mockProvider({ EntityByName: () => undefined });\n const result = await svc.ConfirmAndApply(spec(), 'MJ: NotAnEntity', provider);\n expect(result.Success).toBe(false);\n expect(result.Message).toMatch(/not registered/);\n });\n\n it('cancellation by user yields a friendly failure result, no action invoked', async () => {\n hoisted.dialogResult = 'cancel';\n // Seed action-id lookup so the path past dialog is reachable in case the\n // implementation order changes; with dialog returning 'cancel' the\n // Create action should never be called.\n hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });\n hoisted.actionResponses.set('ACT-GET-ACTIVE', {\n Success: true,\n Message: JSON.stringify({ Active: null, Variants: [] }),\n });\n\n const svc = new InteractiveFormApplyService();\n const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps', mockProvider());\n expect(result.Success).toBe(false);\n expect(result.Message).toMatch(/Cancelled/);\n // Only Get Active Form should have been called (to populate dialog text);\n // Create / Modify should NOT have been called.\n const calls = hoisted.actionCalls.map(c => c.id);\n expect(calls.filter(id => id !== 'ACT-GET-ACTIVE')).toEqual([]);\n });\n\n it('no existing override → calls Create Interactive Form', async () => {\n // RunView lookups: each call resolves the next action ID.\n hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });\n hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-CREATE' }] });\n hoisted.actionResponses.set('ACT-GET-ACTIVE', {\n Success: true,\n Message: JSON.stringify({ Active: null, Variants: [] }),\n });\n hoisted.actionResponses.set('ACT-CREATE', {\n Success: true,\n Message: JSON.stringify({ ComponentID: 'NEW-COMP', OverrideID: 'NEW-OVER', Version: '1.0.0' }),\n });\n\n const svc = new InteractiveFormApplyService();\n const result = await svc.ConfirmAndApply(spec({ name: 'CompactForm' }), 'MJ: Apps', mockProvider());\n\n expect(result.Success).toBe(true);\n expect(result.Mode).toBe('create');\n expect(result.OverrideID).toBe('NEW-OVER');\n expect(result.Version).toBe('1.0.0');\n const calls = hoisted.actionCalls.map(c => c.id);\n expect(calls).toEqual(['ACT-GET-ACTIVE', 'ACT-CREATE']);\n // Success notification surfaced.\n expect(hoisted.notifications.some(n => n.type === 'success')).toBe(true);\n });\n\n it('existing Active override → calls Modify Interactive Form (Pending sibling produced)', async () => {\n hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });\n hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-MODIFY' }] });\n hoisted.actionResponses.set('ACT-GET-ACTIVE', {\n Success: true,\n Message: JSON.stringify({\n Active: { OverrideID: 'OVER-EXISTING', ComponentID: 'COMP-EXISTING', ComponentVersion: '1.0.0' },\n Variants: [],\n }),\n });\n hoisted.actionResponses.set('ACT-MODIFY', {\n Success: true,\n Message: JSON.stringify({ Mode: 'new-version', ComponentID: 'NEW-COMP', OverrideID: 'NEW-OVER', Version: '1.1.0' }),\n });\n\n const svc = new InteractiveFormApplyService();\n const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps', mockProvider());\n\n expect(result.Success).toBe(true);\n expect(result.Mode).toBe('modify-new-version');\n expect(result.Version).toBe('1.1.0');\n const calls = hoisted.actionCalls.map(c => c.id);\n expect(calls).toEqual(['ACT-GET-ACTIVE', 'ACT-MODIFY']);\n });\n\n it('Get Active failure short-circuits with an error', async () => {\n hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });\n hoisted.actionResponses.set('ACT-GET-ACTIVE', {\n Success: false,\n Message: 'DB unavailable',\n });\n\n const svc = new InteractiveFormApplyService();\n const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps', mockProvider());\n expect(result.Success).toBe(false);\n expect(result.Message).toMatch(/Could not check for existing override/);\n });\n\n it('failure on the Create action surfaces the error notification', async () => {\n hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-GET-ACTIVE' }] });\n hoisted.runViewResponses.push({ Success: true, Results: [{ ID: 'ACT-CREATE' }] });\n hoisted.actionResponses.set('ACT-GET-ACTIVE', {\n Success: true,\n Message: JSON.stringify({ Active: null, Variants: [] }),\n });\n hoisted.actionResponses.set('ACT-CREATE', {\n Success: false,\n ResultCode: 'LINT_FAILED',\n Message: 'Spec failed linting.',\n });\n\n const svc = new InteractiveFormApplyService();\n const result = await svc.ConfirmAndApply(spec(), 'MJ: Apps', mockProvider());\n expect(result.Success).toBe(false);\n expect(hoisted.notifications.some(n => n.type === 'error')).toBe(true);\n });\n});\n"]}