Skip to content

Commit 4844620

Browse files
JSONJSON
authored andcommitted
Test cleanup & additions, Added documentation
Remove unused setup in domo.test.ts & add new coverage, Move tests from domo.test.ts into the appropriate test file, document what port1 and port2 are used for.
1 parent d28a262 commit 4844620

8 files changed

Lines changed: 152 additions & 170 deletions

File tree

src/domo.test.ts

Lines changed: 48 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// Mock global location before importing domo
2-
(global as any).location = { search: '' };
3-
41
import Domo, { __mutationObserverCallback } from './domo';
52
import { RequestMethods } from './models/enums/request-methods';
63

@@ -10,76 +7,20 @@ const originalPost = Domo.post;
107
const originalPut = Domo.put;
118
const originalDelete = Domo.delete;
129

13-
declare global {
14-
// eslint-disable-next-line no-var
15-
var _originalXMLHttpRequest: any;
16-
// eslint-disable-next-line no-var
17-
var _openSpy: jest.Mock;
18-
// eslint-disable-next-line no-var
19-
var _xhrInstance: any;
20-
}
21-
22-
// Mock MessagePort and MessageChannel globally for Jest
2310
class MockMessagePort {
2411
onmessage: ((event: any) => void) | null = null;
2512
postMessage = jest.fn();
2613
close = jest.fn();
2714
}
28-
(global as any).MessagePort = MockMessagePort;
2915
(global as any).MessageChannel = class {
3016
port1 = new MockMessagePort();
3117
port2 = new MockMessagePort();
3218
};
3319

34-
// Patch window.addEventListener and removeEventListener to track message listeners for test cleanup
35-
const realAddEventListener = window.addEventListener;
36-
const realRemoveEventListener = window.removeEventListener;
37-
(window as any).eventListeners = { message: [] };
38-
window.addEventListener = function(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions) {
39-
if (type === 'message') {
40-
(window as any).eventListeners.message.push(listener);
41-
}
42-
return realAddEventListener.call(this, type, listener, options);
43-
};
44-
window.removeEventListener = function(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions) {
45-
if (type === 'message') {
46-
const arr = (window as any).eventListeners.message;
47-
const idx = arr.indexOf(listener);
48-
if (idx !== -1) arr.splice(idx, 1);
49-
}
50-
return realRemoveEventListener.call(this, type, listener, options);
51-
};
52-
5320
// Mock browser APIs and global objects as needed
5421
beforeEach(() => {
5522
jest.resetAllMocks();
5623
(window as any)['__RYUU_SID__'] = 'test-token';
57-
window.parent.postMessage = jest.fn();
58-
Object.defineProperty(window.navigator, 'userAgent', {
59-
value: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
60-
configurable: true
61-
});
62-
(window as any)['webkit'] = { messageHandlers: { domofilter: { postMessage: jest.fn() }, domovariable: { postMessage: jest.fn() } } };
63-
// Only redefine MessageChannel if needed for test isolation
64-
(global as any).MessageChannel = (global as any).MessageChannel;
65-
66-
// XMLHttpRequest mock for all HTTP verb tests
67-
globalThis._originalXMLHttpRequest = (global as any).XMLHttpRequest;
68-
globalThis._openSpy = jest.fn();
69-
globalThis._xhrInstance = {
70-
open: globalThis._openSpy,
71-
setRequestHeader: jest.fn(),
72-
send: jest.fn(),
73-
addEventListener: jest.fn(),
74-
removeEventListener: jest.fn(),
75-
onload: null,
76-
onerror: null,
77-
readyState: 4,
78-
status: 200,
79-
response: '{}',
80-
getResponseHeader: jest.fn()
81-
};
82-
(global as any).XMLHttpRequest = jest.fn(() => globalThis._xhrInstance);
8324

8425
Domo.domoHttp = originalDomoHttp;
8526
Domo.get = originalGet;
@@ -88,90 +29,61 @@ beforeEach(() => {
8829
Domo.delete = originalDelete;
8930
});
9031

91-
afterEach(() => {
92-
(global as any).XMLHttpRequest = globalThis._originalXMLHttpRequest;
93-
// Remove all message event listeners to prevent test pollution
94-
const listeners: EventListenerOrEventListenerObject[] = (window as any).eventListeners?.message ?? [];
95-
listeners.forEach((listener: EventListenerOrEventListenerObject) => {
96-
realRemoveEventListener.call(window, 'message', listener);
97-
});
98-
(window as any).eventListeners.message = [];
99-
});
32+
describe('Domo Connect & MessageChannel', () => {
33+
let appDataSpy: jest.Mock,
34+
filtersSpy: jest.Mock,
35+
variablesSpy: jest.Mock;
10036

37+
beforeEach(() => {
38+
Domo.listeners.onAppDataUpdated = [];
39+
Domo.listeners.onFiltersUpdated = [];
40+
Domo.listeners.onVariablesUpdated = [];
10141

102-
describe('domo event/callback APIs', () => {
103-
describe('connect/MessageChannel', () => {
104-
function makeMessageEvent(data: any, ports: any[] = []) {
105-
return { data, ports } as any;
106-
}
42+
appDataSpy = jest.fn();
43+
filtersSpy = jest.fn();
44+
variablesSpy = jest.fn();
45+
46+
Domo.listeners.onAppDataUpdated.push(appDataSpy);
47+
Domo.listeners.onFiltersUpdated.push(filtersSpy);
48+
Domo.listeners.onVariablesUpdated.push(variablesSpy);
49+
});
50+
51+
function makeMessageEvent(data: any, ports: any[] = []) {
52+
return { data, ports } as any;
53+
}
10754

108-
it('should early return if responsePort is undefined', () => {
109-
Domo.connect();
110-
expect(() => Domo.channel.port1.onmessage(makeMessageEvent({ event: 'filtersUpdated', filters: [] }, []))).not.toThrow();
111-
});
55+
it('should early return if responsePort is undefined', () => {
56+
const data = { event: 'filtersUpdated', filters: [] } as any;
57+
Domo.connect();
58+
expect(() => Domo.channel?.port1?.onmessage?.(makeMessageEvent(data, []))).not.toThrow();
59+
});
60+
61+
it('should handle multiple messages correctly', () => {
62+
const channel = new MessageChannel();
63+
const data1 = { event: 'filtersUpdated', filters: [] } as any;
64+
const data2 = { event: 'appData', data: {} } as any;
65+
const data3 = { event: 'variablesUpdated', variables: {} } as any;
66+
67+
expect(() => Domo.channel?.port1?.onmessage?.(makeMessageEvent(data1, [channel.port2]))).not.toThrow();
68+
expect(() => Domo.channel?.port1?.onmessage?.(makeMessageEvent(data2, [channel.port2]))).not.toThrow();
69+
expect(() => Domo.channel?.port1?.onmessage?.(makeMessageEvent(data3, [channel.port2]))).not.toThrow();
70+
expect(variablesSpy).toHaveBeenCalled();
71+
expect(filtersSpy).toHaveBeenCalled();
72+
expect(appDataSpy).toHaveBeenCalled();
73+
expect(channel.port2.postMessage).toHaveBeenCalledTimes(3);
11274
});
11375
});
11476

77+
11578
describe('domo.__util (internal utilities)', () => {
11679
it('should expose the expected private functions', () => {
11780
expect(typeof Domo.__util.isVerifiedOrigin).toBe('function');
11881
expect(typeof Domo.__util.isSuccess).toBe('function');
11982
expect(typeof Domo.__util.getQueryParams).toBe('function');
12083
expect(typeof Domo.__util.setFormatHeaders).toBe('function');
12184
});
122-
123-
it('isSuccess returns true for 2xx, false otherwise', () => {
124-
expect(Domo.__util.isSuccess(200)).toBe(true);
125-
expect(Domo.__util.isSuccess(299)).toBe(true);
126-
expect(Domo.__util.isSuccess(199)).toBe(false);
127-
expect(Domo.__util.isSuccess(300)).toBe(false);
128-
expect(Domo.__util.isSuccess(null)).toBe(false);
129-
expect(Domo.__util.isSuccess(undefined)).toBe(false);
130-
});
131-
132-
it('isVerifiedOrigin honors whitelisting and blacklisting', () => {
133-
expect(Boolean(Domo.__util.isVerifiedOrigin('https://www.domo.com'))).toBe(true);
134-
expect(Boolean(Domo.__util.isVerifiedOrigin('https://www.domotech.io'))).toBe(true);
135-
expect(Boolean(Domo.__util.isVerifiedOrigin('https://www.domorig.io'))).toBe(true);
136-
expect(Boolean(Domo.__util.isVerifiedOrigin('https://domo.demo.domo.com'))).toBe(true);
137-
expect(Boolean(Domo.__util.isVerifiedOrigin('https://qa2staging.fastage1.domotech.io/auth/index'))).toBe(true);
138-
expect(Boolean(Domo.__util.isVerifiedOrigin('https://www.domoapps-test.domo.com'))).toBe(false);
139-
expect(Boolean(Domo.__util.isVerifiedOrigin('https://www.test-domoapps.domo.com'))).toBe(false);
140-
expect(Boolean(Domo.__util.isVerifiedOrigin('https://www.somethingk.com'))).toBe(false);
141-
expect(Boolean(Domo.__util.isVerifiedOrigin('https://www.domo.com.bad.io'))).toBe(false);
142-
expect(Boolean(Domo.__util.isVerifiedOrigin('http://www.domo.com'))).toBe(false);
143-
});
144-
145-
it('getQueryParams parses query string', () => {
146-
Object.defineProperty(global, 'location', {
147-
value: { search: '?foo=bar&baz=qux' },
148-
configurable: true
149-
});
150-
const params = Domo.__util.getQueryParams();
151-
expect('foo' in params).toBe(true);
152-
expect('baz' in params).toBe(true);
153-
expect((params as any)['foo']).toBe('bar');
154-
expect((params as any)['baz']).toBe('qux');
155-
});
156-
157-
it('setFormatHeaders sets Accept header for data/v URLs', () => {
158-
const req = { setRequestHeader: jest.fn() };
159-
Domo.__util.setFormatHeaders(req as any, 'https://domo.com/data/v1', { format: 'array-of-objects' });
160-
expect(req.setRequestHeader).toHaveBeenCalledWith('Accept', expect.any(String));
161-
});
16285
});
16386

164-
describe('domo.env and global exposure', () => {
165-
it('should have env properties with expected types', () => {
166-
expect(typeof Domo.env).toBe('object');
167-
expect(typeof Domo.env.userId).toBeDefined();
168-
});
169-
170-
it('should expose env and __util', () => {
171-
expect(Domo.env).toBeDefined();
172-
expect(Domo.__util).toBeDefined();
173-
});
174-
});
17587

17688
describe('MutationObserver integration', () => {
17789
it('should call handleNode when a new element is added to the DOM', () => {
@@ -186,23 +98,20 @@ describe('MutationObserver integration', () => {
18698
});
18799
});
188100

189-
describe('domo uncovered/miscellaneous branches', () => {
190-
it('should handle catch branch in isVerifiedOrigin', () => {
191-
expect(Domo.__util.isVerifiedOrigin('not a url')).toBe(false);
192-
});
193101

194-
it('should use DataFormats.DEFAULT in setFormatHeaders', () => {
195-
const req = { setRequestHeader: jest.fn() };
196-
Domo.__util.setFormatHeaders(req as any, 'https://domo.com/data/v1', {});
197-
expect(req.setRequestHeader).toHaveBeenCalledWith('Accept', expect.anything());
102+
describe('domo.env and global exposure', () => {
103+
it('should have env properties with expected types', () => {
104+
expect(typeof Domo.env).toBe('object');
105+
expect(typeof Domo.env.userId).toBeDefined();
198106
});
199107

200-
it('should import FilterDataTypes from models/index', () => {
201-
const { FilterDataTypes } = require('../src/models/interfaces/filter-data-types');
202-
expect(FilterDataTypes).toBeDefined();
108+
it('should expose env and __util', () => {
109+
expect(Domo.env).toBeDefined();
110+
expect(Domo.__util).toBeDefined();
203111
});
204112
});
205113

114+
206115
describe('Domo.extend', () => {
207116
beforeEach(() => {
208117
jest.resetModules();
@@ -258,5 +167,4 @@ describe('Domo.extend', () => {
258167
expect(mockHttp).toHaveBeenCalledWith('DELETE', '/grault', undefined);
259168
expect(deleteResult).toBe('mocked-http');
260169
});
261-
262170
});

src/domo.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class Domo {
4747

4848
////////////////////////////////////////////
4949
// Event Listeners
50+
//
51+
// These receive messages from the parent window via port1 of the MessageChannel
5052
//////////////////////////////////////////
5153
static onDataUpdated = onDataUpdated;
5254
static onFiltersUpdated = onFiltersUpdated;
@@ -63,6 +65,8 @@ class Domo {
6365

6466
/////////////////////////////////////////////
6567
// Emitters
68+
//
69+
// These send messages to the parent window via port2 of the MessageChannel
6670
///////////////////////////////////////////
6771
static filterContainer = filterContainer;
6872
static sendVariables = sendVariables;

src/models/enums/data-formats.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
* for more details.
77
*/
88
export enum DataFormats {
9-
DEFAULT = 'application/array-of-objects',
109
ARRAY_OF_OBJECTS = 'application/array-of-objects',
1110
JSON = 'application/json',
1211
CSV = 'text/csv',

src/models/interfaces/query-params.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export interface QueryParams {
66
locale?: string;
77
environment?: string;
88
platform?: 'desktop' | 'mobile';
9+
[key: string]: string | number | undefined;
910
}

src/utils/data-helpers.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,10 @@ import { DomoDataFormats } from "../models/interfaces/domo-data-formats";
99
*/
1010
export function domoFormatToRequestFormat(format: DomoDataFormats): DataFormats {
1111
switch (format) {
12-
case 'array-of-objects': {
13-
return DataFormats.ARRAY_OF_OBJECTS;
14-
}
15-
case 'array-of-arrays': {
16-
return DataFormats.JSON;
17-
}
18-
case 'excel': {
19-
return DataFormats.EXCEL;
20-
}
21-
case 'csv': {
22-
return DataFormats.CSV;
23-
}
24-
default: {
25-
return DataFormats.DEFAULT;
26-
}
12+
case 'array-of-objects': return DataFormats.ARRAY_OF_OBJECTS;
13+
case 'array-of-arrays': return DataFormats.JSON;
14+
case 'excel': return DataFormats.EXCEL;
15+
case 'csv': return DataFormats.CSV;
16+
default: return DataFormats.ARRAY_OF_OBJECTS;
2717
}
2818
}

src/utils/domoutils.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ describe('domoutils', () => {
129129
expect(domoFormatToRequestFormat('array-of-arrays')).toBe(DataFormats.JSON);
130130
expect(domoFormatToRequestFormat('excel')).toBe(DataFormats.EXCEL);
131131
expect(domoFormatToRequestFormat('csv')).toBe(DataFormats.CSV);
132-
expect(domoFormatToRequestFormat('xml')).toBe(DataFormats.DEFAULT);
132+
expect(domoFormatToRequestFormat('xml')).toBe(DataFormats.ARRAY_OF_OBJECTS);
133133
});
134134
});
135135
});

0 commit comments

Comments
 (0)